package com.ganlucode.sipmock.sip.request;

import com.ganlucode.sipmock.config.SipServerProperties;
import com.ganlucode.sipmock.gb28181.auth.DigestServerAuthenticationHelper;
import com.ganlucode.sipmock.gb28181.dto.Device;
import com.ganlucode.sipmock.gb28181.dto.Host;
import com.ganlucode.sipmock.gb28181.dto.request.notify.Notify;
import com.ganlucode.sipmock.sip.mock.entity.CatalogParam;
import com.ganlucode.sipmock.sip.mock.entity.DeviceInfoParam;
import com.ganlucode.sipmock.sip.mock.entity.RegisterParam;
import com.ganlucode.sipmock.utils.SipHelper;
import com.ganlucode.sipmock.utils.XmlUtil;
import com.google.common.collect.Lists;
import gov.nist.javax.sip.header.WWWAuthenticate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.sip.ClientTransaction;
import javax.sip.InvalidArgumentException;
import javax.sip.SipException;
import javax.sip.address.Address;
import javax.sip.address.AddressFactory;
import javax.sip.address.SipURI;
import javax.sip.header.*;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author GanLu
 * @date 2020-05-20 11:53
 */
@Slf4j
@Component
public class SipRequestFactory {

    @Autowired
    private SipHelper sipHelper;

    @Autowired
    private SipServerProperties sipServerProperties;

    @Autowired
    private HeaderFactory headerFactory;

    @Autowired
    private AddressFactory addressFactory;

    @Autowired
    private MessageFactory messageFactory;

    @Autowired
    private DigestServerAuthenticationHelper digestServerAuthenticationHelper;

    private AtomicInteger keepAliveSn = new AtomicInteger(8000);

    public Request createRegisterRequest(RegisterParam registerParam) throws Exception {
        Request request = null;
        // sipuri
        SipURI requestURI = createRequestURI();
        // via
//        List<ViaHeader> viaHeaders =  createViaHeaders("z9hG4bK327999948");
        List<ViaHeader> viaHeaders =  createViaHeaders(null);
        // from
        FromHeader fromHeader = createFromHeader("1457731042");
        // to
        String toTag = null;
        ToHeader toHeader = createToHeader(toTag);
        // callid
        CallIdHeader callIdHeader = sipHelper.getSipProvider().getNewCallId();
        // Forwards
        MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);
        // ceq
        CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L, Request.REGISTER);

        request = messageFactory.createRequest(requestURI, Request.REGISTER, callIdHeader, cSeqHeader, fromHeader,
                toHeader, viaHeaders, maxForwards);
        //contact
        ContactHeader contactHeader = createContactHeader();
        //User-Agent;
        UserAgentHeader userAgentHeader = createUserAgentHeader();
        //expires
        ExpiresHeader expiresHeader = headerFactory.createExpiresHeader(3600);

        request.addHeader(contactHeader);
        request.addHeader(userAgentHeader);
        request.addHeader(expiresHeader);
        if (registerParam != null && registerParam.isWithAuth()) {
            WWWAuthenticateHeader wwwAuthenticateHeader = registerParam.getAuthenticateHeader();
            String response = digestServerAuthenticationHelper
                    .createMd5String(wwwAuthenticateHeader,
                            ((WWWAuthenticate) wwwAuthenticateHeader).getCNonce(),
                            requestURI.toString(),
                            sipServerProperties.getSipServerId(),
                            Request.REGISTER,
                            sipServerProperties.getSipPassword());

            AuthorizationHeader authorizationHeader = headerFactory.createAuthorizationHeader(wwwAuthenticateHeader.getScheme());
            authorizationHeader.setUsername(sipServerProperties.getSipServerId());
            authorizationHeader.setResponse(response);
            authorizationHeader.setRealm(wwwAuthenticateHeader.getRealm());
            authorizationHeader.setNonce(wwwAuthenticateHeader.getNonce());
            authorizationHeader.setURI(requestURI);
            authorizationHeader.setAlgorithm(wwwAuthenticateHeader.getAlgorithm());
            request.addHeader(authorizationHeader);

        }

        return request;
    }

    public <T> Request createMessageRequest(Device device, T content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException {
        Request request = null;
        Host host = device.getHost();
        // sipuri
        SipURI requestURI = addressFactory.createSipURI(device.getDeviceId(), host.getAddress());
        // via
        ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
        ViaHeader viaHeader = headerFactory.createViaHeader(sipServerProperties.getSipIp(), sipServerProperties.getSipPort(),
                device.getTransport(), viaTag);
        viaHeader.setRPort();
        viaHeaders.add(viaHeader);
        // from
        SipURI fromSipURI = addressFactory.createSipURI(sipServerProperties.getSipServerId(),
                sipServerProperties.getSipIp() + ":" + sipServerProperties.getSipPort());
        Address fromAddress = addressFactory.createAddress(fromSipURI);
        FromHeader fromHeader = headerFactory.createFromHeader(fromAddress, fromTag);
        // to
        SipURI toSipURI = addressFactory.createSipURI(device.getDeviceId(), sipServerProperties.getSipDomain());
        Address toAddress = addressFactory.createAddress(toSipURI);
        ToHeader toHeader = headerFactory.createToHeader(toAddress, toTag);
        // callid
        CallIdHeader callIdHeader = sipHelper.getSipProvider().getNewCallId();
        // Forwards
        MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);
        // ceq
        CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L, Request.MESSAGE);

        request = messageFactory.createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
                toHeader, viaHeaders, maxForwards);
        ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("Application", "MANSCDP+xml");
        request.setContent(XmlUtil.beanToXmlWithHeader(content), contentTypeHeader);
        return request;
    }

//    public Request createInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException {
//        Request request = null;
//        Host host = device.getHost();
//        //请求行
//        SipURI requestLine = addressFactory.createSipURI(channelId, host.getAddress());
//        //via
//        ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
//        ViaHeader viaHeader = headerFactory.createViaHeader(sipServerProperties.getSipIp(), sipServerProperties.getSipPort(), device.getTransport(), viaTag);
//        viaHeader.setRPort();
//        viaHeaders.add(viaHeader);
//        //from
//        SipURI fromSipURI = addressFactory.createSipURI(sipServerProperties.getSipServerId(),sipServerProperties.getSipDomain());
//        Address fromAddress = addressFactory.createAddress(fromSipURI);
//        FromHeader fromHeader = headerFactory.createFromHeader(fromAddress, fromTag); //必须要有标记，否则无法创建会话，无法回应ack
//        //to
//        SipURI toSipURI = addressFactory.createSipURI(channelId,sipServerProperties.getSipDomain());
//        Address toAddress = addressFactory.createAddress(toSipURI);
//        ToHeader toHeader = headerFactory.createToHeader(toAddress,null);
//        //callid
//        CallIdHeader callIdHeader = sipHelper.getSipProvider().getNewCallId();
//        //Forwards
//        MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);
//        //ceq
//        CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L, Request.INVITE);
//        request = messageFactory.createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
//
//        Address concatAddress = addressFactory.createAddress(addressFactory.createSipURI(sipServerProperties.getSipServerId(), sipServerProperties.getSipIp()+":"+sipServerProperties.getSipPort()));
//        request.addHeader(headerFactory.createContactHeader(concatAddress));
//
//        ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("Application", "SDP");
//        request.setContent(content, contentTypeHeader);
//        return request;
//    }
//
//    public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag) throws ParseException, InvalidArgumentException {
//        Request request = null;
//        Host host = device.getHost();
//        //请求行
//        SipURI requestLine = addressFactory.createSipURI(device.getDeviceId(), host.getAddress());
//        //via
//        ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
//        ViaHeader viaHeader = headerFactory.createViaHeader(sipServerProperties.getSipIp(), sipServerProperties.getSipPort(), device.getTransport(), viaTag);
//        viaHeader.setRPort();
//        viaHeaders.add(viaHeader);
//        //from
//        SipURI fromSipURI = addressFactory.createSipURI(sipServerProperties.getSipServerId(),sipServerProperties.getSipDomain());
//        Address fromAddress = addressFactory.createAddress(fromSipURI);
//        FromHeader fromHeader = headerFactory.createFromHeader(fromAddress, fromTag); //必须要有标记，否则无法创建会话，无法回应ack
//        //to
//        SipURI toSipURI = addressFactory.createSipURI(channelId,sipServerProperties.getSipDomain());
//        Address toAddress = addressFactory.createAddress(toSipURI);
//        ToHeader toHeader = headerFactory.createToHeader(toAddress,null);
//
//        //callid
//        CallIdHeader callIdHeader = sipHelper.getSipProvider().getNewCallId();
//
//        //Forwards
//        MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);
//
//        //ceq
//        CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L, Request.INVITE);
//        request = messageFactory.createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
//
//        Address concatAddress = addressFactory.createAddress(addressFactory.createSipURI(sipServerProperties.getSipServerId(), sipServerProperties.getSipIp()+":"+sipServerProperties.getSipPort()));
//        request.addHeader(headerFactory.createContactHeader(concatAddress));
//
//        ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("Application", "SDP");
//        request.setContent(content, contentTypeHeader);
//        return request;
//    }

    public Request createDeviceInfoRequest(DeviceInfoParam deviceInfoParam) throws Exception{

        Request request = null;
        // sipuri
        SipURI requestURI = createRequestURI();
        // via
//        List<ViaHeader> viaHeaders =  createViaHeaders("z9hG4bK327999948");
        List<ViaHeader> viaHeaders =  createViaHeaders(null);
        // from
        FromHeader fromHeader = createFromHeader("576330513");
        // to
        String toTag = null;
        ToHeader toHeader = createToHeader(toTag);
        // callid
        CallIdHeader callIdHeader = sipHelper.getSipProvider().getNewCallId();
        // Forwards
        MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);
        // ceq
        CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(20L, Request.MESSAGE);

        request = messageFactory.createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
                toHeader, viaHeaders, maxForwards);
        //User-Agent;
        UserAgentHeader userAgentHeader = createUserAgentHeader();

        request.addHeader(userAgentHeader);
        ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("Application", "MANSCDP+xml");
        request.setContent(XmlUtil.beanToXmlWithHeader(deviceInfoParam.getDeviceInfoResponse()), contentTypeHeader);
        return request;
    }

    public Request createCatalogRequest(CatalogParam catalogParam) throws Exception{

        Request request = null;
        // sipuri
        SipURI requestURI = createRequestURI();
        // via
//        List<ViaHeader> viaHeaders =  createViaHeaders("z9hG4bK2058014622");
        List<ViaHeader> viaHeaders =  createViaHeaders(null);
        // from
        FromHeader fromHeader = createFromHeader("509900875");
        // to
        ToHeader toHeader = createToHeader(null);
        // callid
        CallIdHeader callIdHeader = sipHelper.getSipProvider().getNewCallId();
        // Forwards
        MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);
        // ceq
        CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(20L, Request.MESSAGE);

        request = messageFactory.createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
                toHeader, viaHeaders, maxForwards);
        //User-Agent;
        UserAgentHeader userAgentHeader = createUserAgentHeader();

        request.addHeader(userAgentHeader);
        ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("Application", "MANSCDP+xml");
        request.setContent(XmlUtil.beanToXmlWithHeader(catalogParam.getCatalogResponse(),
                                                       catalogParam.getCatalogResponse().getXstream()),
                contentTypeHeader);
        return request;
    }

    public Request createKeepaliveRequest() throws Exception{
        Request request = null;
        // sipuri
        SipURI requestURI = createRequestURI();
        // via
//        List<ViaHeader> viaHeaders =  createViaHeaders("z9hG4bK2058014622");
        List<ViaHeader> viaHeaders =  createViaHeaders(null);
        // from
        FromHeader fromHeader = createFromHeader("316050254");
        // to
        ToHeader toHeader = createToHeader(null);
        // callid
        CallIdHeader callIdHeader = sipHelper.getSipProvider().getNewCallId();
        // Forwards
        MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);
        // ceq
        CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(20L, Request.MESSAGE);

        request = messageFactory.createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
                toHeader, viaHeaders, maxForwards);
        //User-Agent;
        UserAgentHeader userAgentHeader = createUserAgentHeader();

        request.addHeader(userAgentHeader);
        ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("Application", "MANSCDP+xml");
        Notify notify = Notify.builder()
                .cmdType("Keepalive")
                .sn(String.valueOf(keepAliveSn.incrementAndGet()))
                .deviceID(sipServerProperties.getSipServerId())
                .status("OK")
                .build();

        request.setContent(XmlUtil.beanToXmlWithHeader(notify),
                contentTypeHeader);
        return request;
    }
    public Request createAckRequest(Request request) throws Exception{
        // sipuri
        SipURI requestURI = createRequestURI();
        // from
        FromHeader fromHeader = createFromHeader("live");
        // to
        ToHeader toHeader = createToHeader(null);
        CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
        CSeqHeader cSeqHeader = (CSeqHeader) request.getHeader(CSeqHeader.NAME);
        // via
        List<ViaHeader> viaHeaders =  createViaHeaders(((ViaHeader)request.getHeader(ViaHeader.NAME)).getBranch());
        // Forwards
        MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);
        Request ackRequest = messageFactory.createRequest(requestURI, Request.ACK, callIdHeader, cSeqHeader, fromHeader,
                toHeader, viaHeaders, maxForwards);

        return ackRequest;
    }


    public SipURI createRequestURI() throws ParseException {
        SipURI requestURI = addressFactory.createSipURI(
                sipServerProperties.getSipClientId(),
                sipServerProperties.getSipClientIp()+":"+sipServerProperties.getSipClientPort());
        return requestURI;
    }

    public List<ViaHeader> createViaHeaders(String branch) throws ParseException, InvalidArgumentException {
        List<ViaHeader> viaHeaders = Lists.newArrayList();
        String viaTag = null;
        ViaHeader viaHeader = headerFactory.createViaHeader(
                sipServerProperties.getSipIp(),
                sipServerProperties.getSipPort(),
                sipServerProperties.getClientTransport(),
                branch);
        viaHeader.setRPort();
        viaHeaders.add(viaHeader);
        return viaHeaders;
    }

    public FromHeader createFromHeader(String fromTag) throws ParseException {
        SipURI fromSipURI = addressFactory.createSipURI(sipServerProperties.getSipServerId(),
                sipServerProperties.getSipIp() + ":" + sipServerProperties.getSipPort());
        Address fromAddress = addressFactory.createAddress(fromSipURI);
        FromHeader fromHeader = headerFactory.createFromHeader(fromAddress, fromTag);
        return fromHeader;
    }

    public ToHeader createToHeader(String toTag) throws ParseException {
        SipURI toSipURI = addressFactory.createSipURI(
                sipServerProperties.getSipServerId(),
                sipServerProperties.getSipDomain());
        Address toAddress = addressFactory.createAddress(toSipURI);
        ToHeader toHeader = headerFactory.createToHeader(toAddress, toTag);
        return toHeader;
    }

    public ContactHeader createContactHeader() throws ParseException {
        SipURI contactURI = addressFactory.createSipURI(
                sipServerProperties.getSipServerId(),
                sipServerProperties.getSipIp() + ":" + sipServerProperties.getSipPort());
        Address contactAddress = addressFactory.createAddress(contactURI);
        ContactHeader contactHeader = headerFactory.createContactHeader(contactAddress);
        return contactHeader;
    }

    public UserAgentHeader createUserAgentHeader() throws ParseException {
        List<String> product = Lists.newArrayList();
        product.add("IP Camera");
        UserAgentHeader userAgentHeader = headerFactory.createUserAgentHeader(product);
        return userAgentHeader;
    }

    public ClientTransaction transmitRequest(Request request) throws SipException {
        ClientTransaction clientTransaction = sipHelper.getSipProvider().getNewClientTransaction(request);
        clientTransaction.sendRequest();
        return clientTransaction;
    }

}
